gusucode.com > VC++ 基于IE内核功能很齐全的浏览器(支持多标签)-源码程序 > VC++ 基于IE内核功能很齐全的浏览器(支持多标签)-源码程序/code/Explorer/CJToolBar.cpp
// CJToolBar.cpp : implementation file // Copyright ? 1998-1999 CodeJock.com, All Rights Reserved. // See ReadMe.txt for TERMS OF USE. // ///////////////////////////////////////////////////////////////////////////// /**************************************************************************** * * $Date: 10/30/99 1:56p $ * $Revision: 18 $ * $Archive: /CodeJock/CJLibrary/CJToolBar.cpp $ * * $History: CJToolBar.cpp $ * * ***************** Version 18 ***************** * User: Kirk Stowell Date: 10/30/99 Time: 1:56p * Updated in $/CodeJock/CJLibrary * Removed Invalidate() calls from OnWindowPosChanging(...) to eliminate * screen flicker. * * ***************** Version 17 ***************** * User: Kirk Stowell Date: 10/26/99 Time: 10:51p * Updated in $/CodeJock/CJLibrary * Added CCJFontCombo to the list of controls that can be inserted into a * CCJToolBar. * * ***************** Version 16 ***************** * User: Kirk Stowell Date: 10/24/99 Time: 12:01a * Updated in $/CodeJock/CJLibrary * Fixed potential resource and memory leak problems. * * ***************** Version 15 ***************** * User: Kirk Stowell Date: 9/13/99 Time: 5:45a * Updated in $/CodeJockey/CJLibrary * Fixed initialization bug (small). * * ***************** Version 14 ***************** * User: Kirk Stowell Date: 8/31/99 Time: 1:11a * Updated in $/CodeJockey/CJLibrary * Updated copyright and contact information. * * ***************** Version 13 ***************** * User: Kirk Stowell Date: 8/30/99 Time: 12:06a * Updated in $/CodeJockey/CJLibrary * Added support for CCJComboBoxEx control. * * ***************** Version 12 ***************** * User: Kirk Stowell Date: 8/29/99 Time: 9:14p * Updated in $/CodeJockey/CJLibrary * Added Unicode compliance, thanks to Barry Burton for his help with * this. * * * ***************** Version 10 ***************** * User: Kirk Stowell Date: 7/25/99 Time: 10:00p * Updated in $/CodeJockey/CJLibrary * * ***************** Version 6 ***************** * User: Kirk Stowell Date: 7/18/99 Time: 10:20p * Updated in $/CodeJockey/CJLibrary * Re-wrote toolbar class for vc5 compatibility. Added customization based * on the "Customizable toolbar" article by Doug Keith - * http://www.codeguru.com/toolbar/customizable_tb.shtml. * * Cleaned up inline functions, and import/export macro so that class will * be imported when linked to, and exported at compile time. * * ***************** Version 5 ***************** * User: Kirk Stowell Date: 4/03/99 Time: 11:44p * Updated in $/CodeJockey/CJ60Lib * Added comments and cleaned up code. * * ***************** Version 4 ***************** * User: Kirk Stowell Date: 3/13/99 Time: 11:43p * Updated in $/CodeJockey/CJ60Lib * Changed class to handle both Office and DevStudio style to toolbars. To * use Office toolbars, use the CreateEx() method, otherwise your toolbars * will look like DevStudio. * * ***************** Version 3 ***************** * User: Kirk Stowell Date: 12/14/98 Time: 11:42p * Updated in $/CodeJockey/CJ60Lib * Changed class to derive from Paul DiLascia's CToolBar, this class * adds gripper and control insertion. * * ***************** Version 2 ***************** * User: Kirk Stowell Date: 11/02/98 Time: 11:41p * Updated in $/CodeJockey/CJ60Lib * Fixed bug with DrawNoGripper() method - (Christian Skovdal Andersen). * * ***************** Version 1 ***************** * User: Kirk Stowell Date: 10/17/98 Time: 11:41p * Created in $/CodeJockey/CJ60Lib * Initial re-write and release. * ***************************************************************************/ ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "CJToolBar.h" #include "CJFlatComboBox.h" #include "CJComboBoxEx.h" #include "CJFontCombo.h" #include "CJMenu.h" // CCJMenu class declaration #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define IDC_CUSTOMIZE_BAR 0xFF /* DIBs use RGBQUAD format: 0xbb 0xgg 0xrr 0x00 Reasonably efficient code to convert a COLORREF into an RGBQUAD is byte-order-dependent, so we need different code depending on the byte order we're targeting. */ #define _RGB_TO_RGBQUAD(r,g,b) (RGB(b,g,r)) #define _CLR_TO_RGBQUAD(clr) (RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr))) struct CJX_COLORMAP { // use DWORD instead of RGBQUAD so we can compare two RGBQUADs easily DWORD rgbqFrom; int iSysColorTo; }; const CJX_COLORMAP _sysColorMap[] = { // mapping from color in DIB to system color { _RGB_TO_RGBQUAD(0x00, 0x00, 0x00), COLOR_BTNTEXT }, // black { _RGB_TO_RGBQUAD(0x80, 0x80, 0x80), COLOR_BTNSHADOW }, // dark gray { _RGB_TO_RGBQUAD(0xC0, 0xC0, 0xC0), COLOR_BTNFACE }, // bright gray { _RGB_TO_RGBQUAD(0xFF, 0xFF, 0xFF), COLOR_BTNHIGHLIGHT } // white }; HBITMAP AFXAPI _LoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc, BOOL bMono) { HGLOBAL hglb; if ((hglb = LoadResource(hInst, hRsrc)) == NULL) return NULL; LPBITMAPINFOHEADER lpBitmap = (LPBITMAPINFOHEADER)LockResource(hglb); if (lpBitmap == NULL) return NULL; // make copy of BITMAPINFOHEADER so we can modify the color table const int nColorTableSize = 16; UINT nSize = lpBitmap->biSize + nColorTableSize * sizeof(RGBQUAD); LPBITMAPINFOHEADER lpBitmapInfo = (LPBITMAPINFOHEADER)::malloc(nSize); if (lpBitmapInfo == NULL) return NULL; memcpy(lpBitmapInfo, lpBitmap, nSize); // color table is in RGBQUAD DIB format DWORD* pColorTable = (DWORD*)(((LPBYTE)lpBitmapInfo) + (UINT)lpBitmapInfo->biSize); for (int iColor = 0; iColor < nColorTableSize; iColor++) { // look for matching RGBQUAD color in original for (int i = 0; i < _countof(_sysColorMap); i++) { if (pColorTable[iColor] == _sysColorMap[i].rgbqFrom) { if (bMono) { // all colors except text become white if (_sysColorMap[i].iSysColorTo != COLOR_BTNTEXT) pColorTable[iColor] = _RGB_TO_RGBQUAD(255, 255, 255); } else pColorTable[iColor] = _CLR_TO_RGBQUAD(::GetSysColor(_sysColorMap[i].iSysColorTo)); break; } } } int nWidth = (int)lpBitmapInfo->biWidth; int nHeight = (int)lpBitmapInfo->biHeight; HDC hDCScreen = ::GetDC(NULL); HBITMAP hbm = ::CreateCompatibleBitmap(hDCScreen, nWidth, nHeight); if (hbm != NULL) { HDC hDCGlyphs = ::CreateCompatibleDC(hDCScreen); HBITMAP hbmOld = (HBITMAP)::SelectObject(hDCGlyphs, hbm); LPBYTE lpBits; lpBits = (LPBYTE)(lpBitmap + 1); lpBits += (1 << (lpBitmapInfo->biBitCount)) * sizeof(RGBQUAD); StretchDIBits(hDCGlyphs, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight, lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY); SelectObject(hDCGlyphs, hbmOld); ::DeleteDC(hDCGlyphs); } ::ReleaseDC(NULL, hDCScreen); // free copy of bitmap info struct and resource itself ::free(lpBitmapInfo); ::FreeResource(hglb); return hbm; } #ifdef AFX_INIT_SEG #pragma code_seg(AFX_INIT_SEG) #endif struct AFX_DLLVERSIONINFO { DWORD cbSize; DWORD dwMajorVersion; // Major version DWORD dwMinorVersion; // Minor version DWORD dwBuildNumber; // Build number DWORD dwPlatformID; // DLLVER_PLATFORM_* }; typedef HRESULT (CALLBACK* AFX_DLLGETVERSIONPROC)(AFX_DLLVERSIONINFO *); int _ComCtlVersion = -1; DWORD AFXAPI _GetComCtlVersion() { // return cached version if already determined... if (_ComCtlVersion != -1) return _ComCtlVersion; // otherwise determine comctl32.dll version via DllGetVersion HINSTANCE hInst = ::GetModuleHandleA("COMCTL32.DLL"); ASSERT(hInst != NULL); AFX_DLLGETVERSIONPROC pfn; pfn = (AFX_DLLGETVERSIONPROC)GetProcAddress(hInst, "DllGetVersion"); DWORD dwVersion = VERSION_WIN4; if (pfn != NULL) { AFX_DLLVERSIONINFO dvi; memset(&dvi, 0, sizeof(dvi)); dvi.cbSize = sizeof(dvi); HRESULT hr = (*pfn)(&dvi); if (SUCCEEDED(hr)) { ASSERT(dvi.dwMajorVersion <= 0xFFFF); ASSERT(dvi.dwMinorVersion <= 0xFFFF); dwVersion = MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion); } } _ComCtlVersion = dwVersion; return dwVersion; } struct CJX_CONTROLPOS { int nIndex; int nID; CRect rectOldPos; } ; int _dropDownWidth = -1; int AFXAPI _GetDropDownWidth() { // return cached version if already determined... if (_dropDownWidth != -1) return _dropDownWidth; // otherwise calculate it... HDC hDC = GetDC(NULL); ASSERT(hDC != NULL); HFONT hFont; if ((hFont = ::CreateFont(GetSystemMetrics(SM_CYMENUCHECK), 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett"))) != NULL) { HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont); VERIFY(GetCharWidth(hDC, '6', '6', &_dropDownWidth)); // fix potential resource leak - KStowell - 10-21-99 if (hFont != NULL) { ::SelectObject(hDC, hOldFont); ::DeleteObject(hFont); } } ::ReleaseDC(NULL, hDC); ASSERT(_dropDownWidth != -1); return _dropDownWidth; } ///////////////////////////////////////////////////////////////////////////// // CCJToolBar int CCJToolBar::m_nBarNumber = 0; CCJToolBar::CCJToolBar() { m_bShowDropArrow = false; m_pControls = NULL; m_pDropButtons = NULL; // list of drop-down buttons m_nButtons = 0; m_pBarInfo = NULL; m_hKeyRoot = HKEY_CURRENT_USER; // get a pointer to the application. CWinApp* pApp = AfxGetApp(); ASSERT( pApp ); ++m_nBarNumber; m_strValueName.Format( _T("ToolBar %d"), m_nBarNumber ); m_strSubKey.Format( _T("Software\\%s\\%s\\Settings"), pApp->m_pszRegistryKey, pApp->m_pszProfileName ); // initialize state m_pStringMap = NULL; m_hRsrcImageWell = NULL; m_hInstImageWell = NULL; m_hbmImageWell = NULL; m_bDelayedButtonLayout = TRUE; // default image sizes m_sizeImage.cx = 16; m_sizeImage.cy = 15; // default button sizes m_sizeButton.cx = 23; m_sizeButton.cy = 22; // top and bottom borders are 1 larger than default for ease of grabbing m_cyTopBorder = 3; m_cyBottomBorder = 3; m_bExStyle = false; } CCJToolBar::~CCJToolBar() { AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell); delete m_pStringMap; m_nCount = 0; while (m_pDropButtons) { DROPDOWNBUTTON* pnext = m_pDropButtons->next; delete m_pDropButtons; m_pDropButtons = pnext; } if( m_pControls ) { for( POSITION pos = m_pControls->GetHeadPosition() ; pos ; ) { delete m_pControls->GetNext(pos); } delete m_pControls; } } BOOL CCJToolBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) { return CreateEx(pParentWnd, NULL, dwStyle, CRect(m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder), nID); } BOOL CCJToolBar::CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle, DWORD dwStyle, CRect rcBorders, UINT nID) { ASSERT_VALID(pParentWnd); // must have a parent ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC))); // KStowell - bool for drawing office style gripper. if( dwCtrlStyle != NULL ) m_bExStyle = true; SetBorders(rcBorders); // save the style m_dwStyle = (dwStyle & CBRS_ALL)|( dwStyle & CBRS_GRIPPER ); if (nID == AFX_IDW_TOOLBAR) m_dwStyle |= CBRS_HIDE_INPLACE; dwStyle &= ~CBRS_ALL; dwStyle |= CCS_NOPARENTALIGN|CCS_NOMOVEY|CCS_NODIVIDER|CCS_NORESIZE; dwStyle |= dwCtrlStyle; // initialize common controls #ifdef _VC_VERSION_5 INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(icex); icex.dwICC = ICC_BAR_CLASSES; VERIFY(InitCommonControlsEx(&icex)); #else VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTL_BAR_REG)); #endif _GetComCtlVersion(); ASSERT(_ComCtlVersion != -1); _GetDropDownWidth(); ASSERT(_dropDownWidth != -1); // create the HWND CRect rect; rect.SetRectEmpty(); if (!CWnd::Create(TOOLBARCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID)) return FALSE; // sync up the sizes SetSizes(m_sizeButton, m_sizeImage); // Note: Parent must resize itself for control bar to be resized return TRUE; } BOOL CCJToolBar::PreCreateWindow(CREATESTRUCT& cs) { if( !CCJToolBarBase::PreCreateWindow(cs)) return FALSE; cs.style |= TBSTYLE_TRANSPARENT|TBSTYLE_FLAT; return TRUE; } ////////////////////////////////////////////////////////////////////// // 1999 Kirk Stowell - Inserts a control into the toolbar at the given button id. // CWnd* CCJToolBar::InsertControl( CRuntimeClass* pClass, LPCTSTR lpszWindowName, CRect& rect, UINT nID, DWORD dwStyle ) { CWnd *pCtrl = NULL; if( pClass->IsDerivedFrom( RUNTIME_CLASS( CCJComboBoxEx ))) // CCJComboBoxEx control. { pCtrl = new CCJComboBoxEx; ASSERT_VALID( pCtrl ); if(((CCJComboBoxEx*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } } else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CCJFlatComboBox ))) // CCJFlatComboBox control. { pCtrl = new CCJFlatComboBox; ASSERT_VALID( pCtrl ); if(((CCJFlatComboBox*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } } else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CCJFontCombo ))) // CCJFontCombo control. { pCtrl = new CCJFontCombo; ASSERT_VALID( pCtrl ); if(((CCJFontCombo*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } } else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CComboBox ))) // CComboBox control. { pCtrl = new CComboBox; ASSERT_VALID( pCtrl ); if(((CComboBox*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } } else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CEdit ))) // CEdit control. { pCtrl = new CEdit; ASSERT_VALID( pCtrl ); if(((CEdit*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } } else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CButton ))) // CButton control. { pCtrl = new CButton; ASSERT_VALID( pCtrl ); if(((CButton*)pCtrl)->Create( lpszWindowName, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } } else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CWnd ))) // CWnd object. { pCtrl = new CWnd; ASSERT_VALID( pCtrl ); #ifdef _UNICODE TCHAR szClassName[ 256 ]; MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pClass->m_lpszClassName, -1, szClassName, 255 ); if(((CWnd*)pCtrl)->Create( szClassName, lpszWindowName, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } #else if(((CWnd*)pCtrl)->Create( pClass->m_lpszClassName, lpszWindowName, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) { delete pCtrl; return NULL; } #endif } else // An invalid object was passed in { ASSERT( FALSE ); return NULL; } // if our object list has not been allocated, do it now... if( m_pControls == NULL ) { m_pControls = new CObList(); ASSERT( m_pControls ); } // we have to remember this control, so we can delete it later m_pControls->AddTail( pCtrl ); return InsertControl( pCtrl, rect, nID ); } CWnd* CCJToolBar::InsertControl(CWnd* pCtrl, CRect & rect, UINT nID) { ASSERT_VALID( pCtrl ); // make sure the id is valid, and set the button // style for a seperator. ASSERT( CommandToIndex( nID ) >= 0 ); SetButtonInfo( CommandToIndex( nID ), nID, TBBS_SEPARATOR, rect.Width()); // insert the control into the toolbar. GetItemRect( CommandToIndex(nID), &rect ); pCtrl->SetWindowPos(0, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS ); pCtrl->SetFont( GetFont( )); pCtrl->ShowWindow( SW_SHOW ); return pCtrl; } ////////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // call to add drop-down menus to toolbar buttons. // BOOL CCJToolBar::AddDropDownButton(UINT nIDButton, UINT nIDMenu, BOOL bArrow) { ASSERT_VALID(this); DROPDOWNBUTTON* pb = FindDropDownButton(nIDButton); if (!pb) { pb = new DROPDOWNBUTTON; ASSERT(pb); pb->next = m_pDropButtons; m_pDropButtons = pb; } pb->idButton = nIDButton; pb->idMenu = nIDMenu; int iButton = CommandToIndex(nIDButton); DWORD dwStyle = GetButtonStyle(iButton); dwStyle |= TBSTYLE_DROPDOWN; SetButtonStyle(iButton, dwStyle); if (bArrow) SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); return TRUE; } ////////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // Find buttons structure for given ID // DROPDOWNBUTTON* CCJToolBar::FindDropDownButton(UINT nID) { for (DROPDOWNBUTTON* pb = m_pDropButtons; pb; pb = pb->next) { if (pb->idButton == nID) return pb; } return NULL; } ////////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // Message handler for TBN_DROPDOWN. Default is to display the // specified menu at the right place. You can override to generate dynamic menus // // Args: // - NMTOOLBAR struct from TBN_DROPDOWN // - command id of button // - point to display menu at // #ifndef SEESIIE void CCJToolBar::OnToolBarBtnDropDown(NMHDR* pNMHDR, LRESULT* pRes) { UNUSED_ALWAYS( pRes ); const NMTOOLBAR& nmtb = *(NMTOOLBAR*)pNMHDR; // get location of button CRect rc; GetRect(nmtb.iItem, rc); ClientToScreen(&rc); // call virtual function to display dropdown menu OnDropDownButton(nmtb, nmtb.iItem, rc); } #endif ///////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // Virtual fn you can override to hand drop-down button // events with more friendly args // void CCJToolBar::OnDropDownButton(const NMTOOLBAR& nmtb, UINT nID, CRect rc) { UNUSED_ALWAYS( nID ); DROPDOWNBUTTON* pb = FindDropDownButton(nmtb.iItem); if (pb && pb->idMenu) { // load and display popup menu CCJMenu menu; VERIFY(menu.LoadMenu(pb->idMenu)); CCJMenu* pPopup = (CCJMenu*)menu.GetSubMenu(0); ASSERT(pPopup); pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL, rc.left, rc.bottom, GetParentFrame(), &rc); } } ////////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // Now toolbar has really moved: repaint area beneath old position // void CCJToolBar::OnWindowPosChanged(WINDOWPOS* lpwndpos) { CCJToolBarBase::OnWindowPosChanged(lpwndpos); if (!(lpwndpos->flags & SWP_NOMOVE)) { // if moved: InvalidateOldPos(m_rcOldPos); // invalidate area of old position // Now paint my non-client area at the new location. // Without this, you will still have a partial display bug (try it!) SendMessage(WM_NCPAINT); } } ////////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // Invalidate toolbar rectangle. Because flat toolbars are transparent, // this requires invalidating parent and all siblings that intersect the // rectangle. // void CCJToolBar::InvalidateOldPos(const CRect& rcInvalid) { // make parent paint the area beneath rectangle CWnd* pParent = GetParent(); // parent (dock bar/frame) window ASSERT_VALID(pParent); // check CRect rc( rcInvalid ); // copy rectangle pParent->ScreenToClient(&rc); // convert to parent client coords pParent->InvalidateRect(&rc); // invalidate // now do same for each sibling too for (CWnd* pSib = pParent->GetWindow(GW_CHILD); pSib; pSib=pSib->GetNextWindow(GW_HWNDNEXT)) { CRect rc; // window rect of sibling pSib->GetWindowRect(&rc); // ... if (rc.IntersectRect(rc, rcInvalid)) { // if intersects invalid rect pSib->ScreenToClient(&rc); // convert to sibling coords pSib->InvalidateRect(&rc); // invalidate pSib->SendMessage(WM_NCPAINT); // nonclient area too! } } } ////////////////// // 1997 Microsoft Systems Journal - Written by Paul DiLascia. // This is the all-important function that gets the true size of a button, // instead of using m_sizeButton. And it's virtual, so you can override if // my algorithm doesn't work, as will surely be the case in some circumstances. // CSize CCJToolBar::GetButtonSize(TBBUTTON* pData, int iButton) { ASSERT(_ComCtlVersion > 0); // Get the actual size of the button, not what's in m_sizeButton. // Make sure to do SendMessage instead of calling MFC's GetItemRect, // which has all sorts of bad side-effects! (Go ahead, take a look at it.) // CRect rc; SendMessage(TB_GETITEMRECT, iButton, (LPARAM)&rc); CSize sz = rc.Size(); //////////////// // Now must do special case for various versions of comctl32.dll, // DWORD dwStyle = pData[iButton].fsStyle; if ((pData[iButton].fsState & TBSTATE_WRAP)) { if (dwStyle & TBSTYLE_SEP) { // this is the last separator in the row (eg vertically docked) // fudge the height, and ignore the width. TB_GETITEMRECT will return // size = (8 x 22) even for a separator in vertical toolbar // if (_ComCtlVersion <= VERSION_IE3) sz.cy -= 3; // empircally good fudge factor else if (_ComCtlVersion != VERSION_IE4) sz.cy = sz.cx; sz.cx = 0; // separator takes no width if it's the last one } else if (dwStyle & TBSTYLE_DROPDOWN && ( m_bShowDropArrow == false )) { // ignore width of dropdown sz.cx = 0; } } return sz; } // This function saves the state (visible buttons, toolbar position, etc.) // of the toolbar, using the registry key provided to the Create(...) function. void CCJToolBar::SaveState() { // if there is an associated registry subkey if (m_strSubKey.GetLength()) { // save the toolbar state to the registry GetToolBarCtrl().SaveState( m_hKeyRoot, m_strSubKey, m_strValueName ); } } // This function restores the state (visible buttons, toolbar position, etc.) // of the toolbar, using the registry key provided to the Create(...) function. void CCJToolBar::RestoreState() { // if there is an associated registry subkey if (m_strSubKey.GetLength()) { // restore the toolbar state from the registry GetToolBarCtrl().RestoreState( m_hKeyRoot, m_strSubKey, m_strValueName ); } } // This function is called when the user begins dragging a toolbar // button or when the customization dialog is being populated with // toolbar information. Basically, *pResult should be populated with // your answer to the question, "is the user allowed to delete this // button?". void CCJToolBar::OnToolBarQueryDelete(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); // if we're not floating - user can delete anything *pResult = !IsFloating(); } // This function is called when the user begins dragging a toolbar // button or when the customization dialog is being populated with // toolbar information. Basically, *pResult should be populated with // your answer to the question, "is the user allowed to insert a // button to the left of this one?". void CCJToolBar::OnToolBarQueryInsert(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); // if we're not floating - user can insert anywhere *pResult = !IsFloating(); } // This function is called whenever the user makes a change to the // layout of the toolbar. Calling the mainframe's RecalcLayout forces // the toolbar to repaint itself. void CCJToolBar::OnToolBarChange(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); // force the frame window to recalculate the size GetParentFrame()->RecalcLayout(); } // This function is called when the user begins dragging a toolbar button. void CCJToolBar::OnToolBarBeginDrag(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); } // This function is called when the user has completed a dragging operation. void CCJToolBar::OnToolBarEndDrag(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); } // This function is called when the user initially calls up the toolbar // customization dialog box. void CCJToolBar::OnToolBarBeginAdjust(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); } // This function is called when the user clicks on the help button on the // toolbar customization dialog box. void CCJToolBar::OnToolBarCustomHelp(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); } // This function is called when the user dismisses the toolbar customization // dialog box. void CCJToolBar::OnToolBarEndAdjust(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); // save the state of the toolbar for reinitialization SaveState(); } // This function is called to populate the toolbar customization dialog box // with information regarding all of the possible toolbar buttons. void CCJToolBar::OnToolBarGetButtonInfo(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pResult ); TBNOTIFY* tbStruct; // data needed by customize dialog box // init the pointer tbStruct = (TBNOTIFY *)pNMHDR; // if the index is valid if (0 <= tbStruct->iItem && tbStruct->iItem < m_nButtons) { // copy the stored button structure tbStruct->tbButton = m_pBarInfo[tbStruct->iItem].tbButton; // copy the text for the button label in the dialog _tcscpy( tbStruct->pszText, m_pBarInfo[tbStruct->iItem].btnText ); // indicate valid data was sent *pResult = TRUE; } // else there is no button for this index else { *pResult = FALSE; } } // This function is called when the user clicks on the reset button on the // toolbar customization dialog box. void CCJToolBar::OnToolBarReset(NMHDR *pNMHDR, LRESULT *pResult) { UNUSED_ALWAYS( pNMHDR ); UNUSED_ALWAYS( pResult ); // restore the toolbar to the way it was before entering customization RestoreState(); } void CCJToolBar::OnDestroy() { // save the current state of the toolbar SaveState(); CCJToolBarBase::OnDestroy(); } void CCJToolBar::OnRButtonDown(UINT nFlags, CPoint point) { if( m_pBarInfo ) { CPoint pt( point ); ClientToScreen( &pt ); // load and display popup menu CCJMenu popupMenu; VERIFY(popupMenu.CreatePopupMenu( )); popupMenu.InsertMenu(0, MF_BYPOSITION, IDC_CUSTOMIZE_BAR, _T("&Customize...")); int nResult = popupMenu.TrackPopupMenu( TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL|TPM_RETURNCMD, pt.x, pt.y, this ); if( nResult == IDC_CUSTOMIZE_BAR ) { // open the customization dialog. GetToolBarCtrl().Customize(); } } else { CCJToolBarBase::OnRButtonDown(nFlags, point); } } ///////////////////////////////////////////////////////////////////////////// // The remainder of this class is mostly copied straight from MFC ( vc6 ), // except for the modifications which are commented... ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // CCJToolBar BOOL CCJToolBar::OnNcCreate(LPCREATESTRUCT lpCreateStruct) { // If the toolbar is inside a ReBar, we need to make the parent frame // the owner so it will get notifications. CWnd* pParent = GetParent(); ASSERT(pParent); TCHAR classname[64]; GetClassName(pParent->m_hWnd, classname, _countof(classname)); if (_tcscmp(classname, REBARCLASSNAME)==0) { CFrameWnd* pFrame = GetParentFrame(); ASSERT_VALID(pFrame); SetOwner(pFrame); m_bInReBar = true; m_bExStyle = false; } if (!CCJToolBarBase::OnNcCreate(lpCreateStruct)) return FALSE; // if the owner was set before the toolbar was created, set it now if (m_hWndOwner != NULL) DefWindowProc(TB_SETPARENT, (WPARAM)m_hWndOwner, 0); DefWindowProc(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); return TRUE; } void CCJToolBar::SetOwner(CWnd* pOwnerWnd) { ASSERT_VALID(this); if (m_hWnd != NULL) { ASSERT(::IsWindow(m_hWnd)); DefWindowProc(TB_SETPARENT, (WPARAM)pOwnerWnd->GetSafeHwnd(), 0); } CCJToolBarBase::SetOwner(pOwnerWnd); } void CCJToolBar::SetSizes(SIZE sizeButton, SIZE sizeImage) { ASSERT_VALID(this); // sizes must be non-zero and positive ASSERT(sizeButton.cx > 0 && sizeButton.cy > 0); ASSERT(sizeImage.cx > 0 && sizeImage.cy > 0); // button must be big enough to hold image // + 7 pixels on x // + 6 pixels on y ASSERT(sizeButton.cx >= sizeImage.cx + 7); ASSERT(sizeButton.cy >= sizeImage.cy + 6); if (::IsWindow(m_hWnd)) { // set the sizes via TB_SETBITMAPSIZE and TB_SETBUTTONSIZE VERIFY(SendMessage(TB_SETBITMAPSIZE, 0, MAKELONG(sizeImage.cx, sizeImage.cy))); VERIFY(SendMessage(TB_SETBUTTONSIZE, 0, MAKELONG(sizeButton.cx, sizeButton.cy))); Invalidate(); // just to be nice if called when toolbar is visible } else { // just set our internal values for later m_sizeButton = sizeButton; m_sizeImage = sizeImage; } } void CCJToolBar::SetHeight(int cyHeight) { ASSERT_VALID(this); int nHeight = cyHeight; if (m_dwStyle & CBRS_BORDER_TOP) cyHeight -= afxData.cyBorder2; if (m_dwStyle & CBRS_BORDER_BOTTOM) cyHeight -= afxData.cyBorder2; m_cyBottomBorder = (cyHeight - m_sizeButton.cy) / 2; // if there is an extra pixel, m_cyTopBorder will get it m_cyTopBorder = cyHeight - m_sizeButton.cy - m_cyBottomBorder; if (m_cyTopBorder < 0) { TRACE1("Warning: CCJToolBar::SetHeight(%d) is smaller than button.\n", nHeight); m_cyBottomBorder += m_cyTopBorder; m_cyTopBorder = 0; // will clip at bottom } // recalculate the non-client region SetWindowPos(NULL, 0, 0, 0, 0, SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOZORDER); Invalidate(); // just to be nice if called when toolbar is visible } struct CToolBarData { WORD wVersion; WORD wWidth; WORD wHeight; WORD wItemCount; //WORD aItems[wItemCount] WORD* items() { return (WORD*)(this+1); } }; BOOL CCJToolBar::LoadToolBar(LPCTSTR lpszResourceName, TOOLBARINFO* pBarInfo) { ASSERT_VALID(this); ASSERT(lpszResourceName != NULL); // determine location of the bitmap in resource fork HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR); HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR); if (hRsrc == NULL) return FALSE; HGLOBAL hGlobal = LoadResource(hInst, hRsrc); if (hGlobal == NULL) return FALSE; CToolBarData* pData = (CToolBarData*)LockResource(hGlobal); if (pData == NULL) return FALSE; ASSERT(pData->wVersion == 1); UINT* pItems = new UINT[pData->wItemCount]; for (int i = 0; i < pData->wItemCount; i++) pItems[i] = pData->items()[i]; BOOL bResult = SetButtons(pItems, pData->wItemCount); delete[] pItems; if (bResult) { // set new sizes of the buttons CSize sizeImage(pData->wWidth, pData->wHeight); CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7); SetSizes(sizeButton, sizeImage); // load bitmap now that sizes are known by the toolbar control bResult = LoadBitmap(lpszResourceName); } UnlockResource(hGlobal); FreeResource(hGlobal); // is the toolbar is customizable? if(( pBarInfo ) && ( bResult )) { m_pBarInfo = pBarInfo; m_nButtons = GetToolBarCtrl().GetButtonCount(); // modify the style to include adjustable ModifyStyle(0, CCS_ADJUSTABLE); RestoreState(); } return bResult; } BOOL CCJToolBar::LoadBitmap(LPCTSTR lpszResourceName) { ASSERT_VALID(this); ASSERT(lpszResourceName != NULL); // determine location of the bitmap in resource fork HINSTANCE hInstImageWell = AfxFindResourceHandle(lpszResourceName, RT_BITMAP); HRSRC hRsrcImageWell = ::FindResource(hInstImageWell, lpszResourceName, RT_BITMAP); if (hRsrcImageWell == NULL) return FALSE; // load the bitmap HBITMAP hbmImageWell; hbmImageWell = _LoadSysColorBitmap(hInstImageWell, hRsrcImageWell); // tell common control toolbar about the new bitmap if (!AddReplaceBitmap(hbmImageWell)) return FALSE; // remember the resource handles so the bitmap can be recolored if necessary m_hInstImageWell = hInstImageWell; m_hRsrcImageWell = hRsrcImageWell; return TRUE; } BOOL CCJToolBar::SetBitmap(HBITMAP hbmImageWell) { ASSERT_VALID(this); ASSERT(hbmImageWell != NULL); // the caller must manage changing system colors m_hInstImageWell = NULL; m_hRsrcImageWell = NULL; // tell common control toolbar about the new bitmap return AddReplaceBitmap(hbmImageWell); } BOOL CCJToolBar::AddReplaceBitmap(HBITMAP hbmImageWell) { // need complete bitmap size to determine number of images BITMAP bitmap; VERIFY(::GetObject(hbmImageWell, sizeof(BITMAP), &bitmap)); // add the bitmap to the common control toolbar BOOL bResult; if (m_hbmImageWell == NULL) { TBADDBITMAP addBitmap; addBitmap.hInst = NULL; // makes TBADDBITMAP::nID behave a HBITMAP addBitmap.nID = (UINT)hbmImageWell; bResult = DefWindowProc(TB_ADDBITMAP, bitmap.bmWidth / m_sizeImage.cx, (LPARAM)&addBitmap) == 0; } else { TBREPLACEBITMAP replaceBitmap; replaceBitmap.hInstOld = NULL; replaceBitmap.nIDOld = (UINT)m_hbmImageWell; replaceBitmap.hInstNew = NULL; replaceBitmap.nIDNew = (UINT)hbmImageWell; replaceBitmap.nButtons = bitmap.bmWidth / m_sizeImage.cx; bResult = (BOOL)DefWindowProc(TB_REPLACEBITMAP, 0, (LPARAM)&replaceBitmap); } // remove old bitmap, if present if (bResult) { AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell); m_hbmImageWell = hbmImageWell; } return bResult; } BOOL CCJToolBar::SetButtons(const UINT* lpIDArray, int nIDCount) { ASSERT_VALID(this); ASSERT(nIDCount >= 1); // must be at least one of them ASSERT(lpIDArray == NULL || AfxIsValidAddress(lpIDArray, sizeof(UINT) * nIDCount, FALSE)); // delete all existing buttons int nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0); while (nCount--) VERIFY(DefWindowProc(TB_DELETEBUTTON, 0, 0)); TBBUTTON button; memset(&button, 0, sizeof(TBBUTTON)); button.iString = -1; if (lpIDArray != NULL) { // add new buttons to the common control int iImage = 0; for (int i = 0; i < nIDCount; i++) { button.fsState = TBSTATE_ENABLED; if ((button.idCommand = *lpIDArray++) == 0) { // separator button.fsStyle = TBSTYLE_SEP; // width of separator includes 8 pixel overlap ASSERT(_ComCtlVersion != -1); if ((GetStyle() & TBSTYLE_FLAT) || _ComCtlVersion == VERSION_IE4) button.iBitmap = 6; else button.iBitmap = 8; } else { // a command button with image button.fsStyle = TBSTYLE_BUTTON; button.iBitmap = iImage++; } if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button)) return FALSE; } } else { // add 'blank' buttons button.fsState = TBSTATE_ENABLED; for (int i = 0; i < nIDCount; i++) { ASSERT(button.fsStyle == TBSTYLE_BUTTON); if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button)) return FALSE; } } m_nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0); m_bDelayedButtonLayout = TRUE; return TRUE; } #ifdef AFX_CORE3_SEG #pragma code_seg(AFX_CORE3_SEG) #endif ///////////////////////////////////////////////////////////////////////////// // CCJToolBar attribute access void CCJToolBar::GetButton(int nIndex, TBBUTTON* pButton) const { CCJToolBar* pBar = (CCJToolBar*)this; VERIFY(pBar->DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)pButton)); // TBSTATE_ENABLED == TBBS_DISABLED so invert it pButton->fsState ^= TBSTATE_ENABLED; } void CCJToolBar::SetButton(int nIndex, TBBUTTON* pButton) { // get original button state TBBUTTON button; VERIFY(DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)&button)); // prepare for old/new button comparsion button.bReserved[0] = 0; button.bReserved[1] = 0; // TBSTATE_ENABLED == TBBS_DISABLED so invert it pButton->fsState ^= TBSTATE_ENABLED; pButton->bReserved[0] = 0; pButton->bReserved[1] = 0; // nothing to do if they are the same if (memcmp(pButton, &button, sizeof(TBBUTTON)) != 0) { // don't redraw everything while setting the button DWORD dwStyle = GetStyle(); ModifyStyle(WS_VISIBLE, 0); VERIFY(DefWindowProc(TB_DELETEBUTTON, nIndex, 0)); VERIFY(DefWindowProc(TB_INSERTBUTTON, nIndex, (LPARAM)pButton)); ModifyStyle(0, dwStyle & WS_VISIBLE); // invalidate appropriate parts if (((pButton->fsStyle ^ button.fsStyle) & TBSTYLE_SEP) || ((pButton->fsStyle & TBSTYLE_SEP) && pButton->iBitmap != button.iBitmap)) { // changing a separator Invalidate(); } else { // invalidate just the button CRect rect; if (DefWindowProc(TB_GETITEMRECT, nIndex, (LPARAM)&rect)) InvalidateRect(rect); } } } int CCJToolBar::CommandToIndex(UINT nIDFind) const { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); CCJToolBar* pBar = (CCJToolBar*)this; return (int)pBar->DefWindowProc(TB_COMMANDTOINDEX, nIDFind, 0); } UINT CCJToolBar::GetItemID(int nIndex) const { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); TBBUTTON button; GetButton(nIndex, &button); return button.idCommand; } void CCJToolBar::GetItemRect(int nIndex, LPRECT lpRect) const { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); // handle any delayed layout if (m_bDelayedButtonLayout) ((CCJToolBar*)this)->Layout(); // now it is safe to get the item rectangle CCJToolBar* pBar = (CCJToolBar*)this; if (!pBar->DefWindowProc(TB_GETITEMRECT, nIndex, (LPARAM)lpRect)) SetRectEmpty(lpRect); } void CCJToolBar::Layout() { ASSERT(m_bDelayedButtonLayout); m_bDelayedButtonLayout = FALSE; BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0; if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC)) ((CCJToolBar*)this)->CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH | LM_COMMIT); else if (bHorz) ((CCJToolBar*)this)->CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK | LM_COMMIT); else ((CCJToolBar*)this)->CalcDynamicLayout(0, LM_VERTDOCK | LM_COMMIT); } UINT CCJToolBar::GetButtonStyle(int nIndex) const { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); TBBUTTON button; GetButton(nIndex, &button); return MAKELONG(button.fsStyle, button.fsState); } void CCJToolBar::SetButtonStyle(int nIndex, UINT nStyle) { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); TBBUTTON button; GetButton(nIndex, &button); if (button.fsStyle != (BYTE)LOWORD(nStyle) || button.fsState != (BYTE)HIWORD(nStyle)) { button.fsStyle = (BYTE)LOWORD(nStyle); button.fsState = (BYTE)HIWORD(nStyle); SetButton(nIndex, &button); m_bDelayedButtonLayout = TRUE; } } #define CX_OVERLAP 0 CSize CCJToolBar::CalcSize(TBBUTTON* pData, int nCount) { ASSERT(pData != NULL && nCount > 0); CPoint cur(0,0); CSize sizeResult(0,0); int cyTallestOnRow = 0; for (int i = 0; i < nCount; i++) { if (pData[i].fsState & TBSTATE_HIDDEN) continue; // **PD** // Load actual size of button into a local variable // called m_sizeButton. C++ will use this instead of // CToolBar::m_sizeButton. // CSize m_sizeButton = GetButtonSize(pData, i); // **PD** // I also changed the logic below to be more correct. cyTallestOnRow = max(cyTallestOnRow, m_sizeButton.cy); sizeResult.cx = max(cur.x + m_sizeButton.cx, sizeResult.cx); sizeResult.cy = max(cur.y + m_sizeButton.cy, sizeResult.cy); cur.x += m_sizeButton.cx - CX_OVERLAP; if (pData[i].fsState & TBSTATE_WRAP) { cur.x = 0; cur.y += cyTallestOnRow; cyTallestOnRow = 0; if (pData[i].fsStyle & TBSTYLE_SEP) cur.y += m_sizeButton.cy; } } return sizeResult; } int CCJToolBar::WrapToolBar(TBBUTTON* pData, int nCount, int nWidth) { ASSERT(pData != NULL && nCount > 0); int nResult = 0; int x = 0; for (int i = 0; i < nCount; i++) { pData[i].fsState &= ~TBSTATE_WRAP; if (pData[i].fsState & TBSTATE_HIDDEN) continue; int dx, dxNext; // **PD** // Load actual size of button into a local variable // called m_sizeButton. C++ will use this instead of // CToolBar::m_sizeButton. // CSize m_sizeButton = GetButtonSize(pData, i); dx = m_sizeButton.cx; dxNext = dx - CX_OVERLAP; if (x + dx > nWidth) { BOOL bFound = FALSE; for (int j = i; j >= 0 && !(pData[j].fsState & TBSTATE_WRAP); j--) { // Find last separator that isn't hidden // a separator that has a command ID is not // a separator, but a custom control. if ((pData[j].fsStyle & TBSTYLE_SEP) && (pData[j].idCommand == 0) && !(pData[j].fsState & TBSTATE_HIDDEN)) { bFound = TRUE; i = j; x = 0; pData[j].fsState |= TBSTATE_WRAP; nResult++; break; } } if (!bFound) { for (int j = i - 1; j >= 0 && !(pData[j].fsState & TBSTATE_WRAP); j--) { // Never wrap anything that is hidden, // or any custom controls if ((pData[j].fsState & TBSTATE_HIDDEN) || ((pData[j].fsStyle & TBSTYLE_SEP) && (pData[j].idCommand != 0))) continue; bFound = TRUE; i = j; x = 0; pData[j].fsState |= TBSTATE_WRAP; nResult++; break; } if (!bFound) x += dxNext; } } else x += dxNext; } return nResult + 1; } void CCJToolBar::SizeToolBar(TBBUTTON* pData, int nCount, int nLength, BOOL bVert) { ASSERT(pData != NULL && nCount > 0); if (!bVert) { int nMin, nMax, nTarget, nCurrent, nMid; // Wrap ToolBar as specified nMax = nLength; nTarget = WrapToolBar(pData, nCount, nMax); // Wrap ToolBar vertically nMin = 0; nCurrent = WrapToolBar(pData, nCount, nMin); if (nCurrent != nTarget) { while (nMin < nMax) { nMid = (nMin + nMax) / 2; nCurrent = WrapToolBar(pData, nCount, nMid); if (nCurrent == nTarget) nMax = nMid; else { if (nMin == nMid) { WrapToolBar(pData, nCount, nMax); break; } nMin = nMid; } } } CSize size = CalcSize(pData, nCount); WrapToolBar(pData, nCount, size.cx); } else { CSize sizeMax, sizeMin, sizeMid; // Wrap ToolBar vertically WrapToolBar(pData, nCount, 0); sizeMin = CalcSize(pData, nCount); // Wrap ToolBar horizontally WrapToolBar(pData, nCount, 32767); sizeMax = CalcSize(pData, nCount); while (sizeMin.cx < sizeMax.cx) { sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2; WrapToolBar(pData, nCount, sizeMid.cx); sizeMid = CalcSize(pData, nCount); if (nLength < sizeMid.cy) { if (sizeMin == sizeMid) { WrapToolBar(pData, nCount, sizeMax.cx); return; } sizeMin = sizeMid; } else if (nLength > sizeMid.cy) sizeMax = sizeMid; else return; } } } CSize CCJToolBar::CalcLayout(DWORD dwMode, int nLength) { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); if (dwMode & LM_HORZDOCK) ASSERT(dwMode & LM_HORZ); int nCount; TBBUTTON* pData = NULL; CSize sizeResult(0,0); //BLOCK: Load Buttons { nCount = DefWindowProc(TB_BUTTONCOUNT, 0, 0); if (nCount != 0) { int i; pData = new TBBUTTON[nCount]; for (i = 0; i < nCount; i++) GetButton(i, &pData[i]); } } if (nCount > 0) { if (!(m_dwStyle & CBRS_SIZE_FIXED)) { BOOL bDynamic = m_dwStyle & CBRS_SIZE_DYNAMIC; if (bDynamic && (dwMode & LM_MRUWIDTH)) SizeToolBar(pData, nCount, m_nMRUWidth); else if (bDynamic && (dwMode & LM_HORZDOCK)) SizeToolBar(pData, nCount, 32767); else if (bDynamic && (dwMode & LM_VERTDOCK)) SizeToolBar(pData, nCount, 0); else if (bDynamic && (nLength != -1)) { CRect rect; rect.SetRectEmpty(); CalcInsideRect(rect, (dwMode & LM_HORZ)); BOOL bVert = (dwMode & LM_LENGTHY); int nLen = nLength + (bVert ? rect.Height() : rect.Width()); SizeToolBar(pData, nCount, nLen, bVert); } else if (bDynamic && (m_dwStyle & CBRS_FLOATING)) SizeToolBar(pData, nCount, m_nMRUWidth); else SizeToolBar(pData, nCount, (dwMode & LM_HORZ) ? 32767 : 0); } sizeResult = CalcSize(pData, nCount); if (dwMode & LM_COMMIT) { CJX_CONTROLPOS* pControl = NULL; int nControlCount = 0; BOOL bIsDelayed = m_bDelayedButtonLayout; m_bDelayedButtonLayout = FALSE; for (int i = 0; i < nCount; i++) if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0)) nControlCount++; if (nControlCount > 0) { pControl = new CJX_CONTROLPOS[nControlCount]; nControlCount = 0; for(int i = 0; i < nCount; i++) { if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0)) { pControl[nControlCount].nIndex = i; pControl[nControlCount].nID = pData[i].idCommand; CRect rect; GetItemRect(i, &rect); ClientToScreen(&rect); pControl[nControlCount].rectOldPos = rect; nControlCount++; } } } if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC)) m_nMRUWidth = sizeResult.cx; for (i = 0; i < nCount; i++) SetButton(i, &pData[i]); if (nControlCount > 0) { for (int i = 0; i < nControlCount; i++) { CWnd* pWnd = GetDlgItem(pControl[i].nID); if (pWnd != NULL) { CRect rect; pWnd->GetWindowRect(&rect); CPoint pt = rect.TopLeft() - pControl[i].rectOldPos.TopLeft(); GetItemRect(pControl[i].nIndex, &rect); pt = rect.TopLeft() + pt; pWnd->SetWindowPos(NULL, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); } } delete[] pControl; } m_bDelayedButtonLayout = bIsDelayed; } delete[] pData; } //BLOCK: Adjust Margins { CRect rect; rect.SetRectEmpty(); CalcInsideRect(rect, (dwMode & LM_HORZ)); sizeResult.cy -= rect.Height(); sizeResult.cx -= rect.Width(); CSize size = CCJToolBarBase::CalcFixedLayout((dwMode & LM_STRETCH), (dwMode & LM_HORZ)); sizeResult.cx = max(sizeResult.cx, size.cx); sizeResult.cy = max(sizeResult.cy, size.cy); } return sizeResult; } CSize CCJToolBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz) { DWORD dwMode = bStretch ? LM_STRETCH : 0; dwMode |= bHorz ? LM_HORZ : 0; return CalcLayout(dwMode); } CSize CCJToolBar::CalcDynamicLayout(int nLength, DWORD dwMode) { if ((nLength == -1) && !(dwMode & LM_MRUWIDTH) && !(dwMode & LM_COMMIT) && ((dwMode & LM_HORZDOCK) || (dwMode & LM_VERTDOCK))) { return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZDOCK); } return CalcLayout(dwMode, nLength); } void CCJToolBar::GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); TBBUTTON button; GetButton(nIndex, &button); nID = button.idCommand; nStyle = MAKELONG(button.fsStyle, button.fsState); iImage = button.iBitmap; } void CCJToolBar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage) { ASSERT_VALID(this); TBBUTTON button; GetButton(nIndex, &button); TBBUTTON save; memcpy(&save, &button, sizeof(save)); button.idCommand = nID; button.iBitmap = iImage; button.fsStyle = (BYTE)LOWORD(nStyle); button.fsState = (BYTE)HIWORD(nStyle); if (memcmp(&save, &button, sizeof(save)) != 0) { SetButton(nIndex, &button); m_bDelayedButtonLayout = TRUE; } } int CCJToolBar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const { ASSERT_VALID(this); ASSERT(::IsWindow(m_hWnd)); // check child windows first by calling CCJToolBarBase int nHit = CCJToolBarBase::OnToolHitTest(point, pTI); if (nHit != -1) return nHit; // now hit test against CCJToolBar buttons CCJToolBar* pBar = (CCJToolBar*)this; int nButtons = (int)pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0); for (int i = 0; i < nButtons; i++) { CRect rect; TBBUTTON button; if (pBar->DefWindowProc(TB_GETITEMRECT, i, (LPARAM)&rect)) { ++rect.bottom; ++rect.right; if (rect.PtInRect(point) && pBar->DefWindowProc(TB_GETBUTTON, i, (LPARAM)&button) && !(button.fsStyle & TBSTYLE_SEP)) { int nHit = GetItemID(i); if (pTI != NULL && pTI->cbSize >= sizeof(AFX_OLDTOOLINFO)) { pTI->hwnd = m_hWnd; pTI->rect = rect; pTI->uId = nHit; pTI->lpszText = LPSTR_TEXTCALLBACK; } // found matching rect, return the ID of the button return nHit != 0 ? nHit : -1; } } } return -1; } BOOL CCJToolBar::SetButtonText(int nIndex, LPCTSTR lpszText) { // attempt to lookup string index in map int nString = -1; void* p; if (m_pStringMap != NULL && m_pStringMap->Lookup(lpszText, p)) nString = (int)p; // add new string if not already in map if (nString == -1) { // initialize map if necessary if (m_pStringMap == NULL) m_pStringMap = new CMapStringToPtr; // add new string to toolbar list CString strTemp(lpszText, lstrlen(lpszText)+1); nString = (int)DefWindowProc(TB_ADDSTRING, 0, (LPARAM)(LPCTSTR)strTemp); if (nString == -1) return FALSE; // cache string away in string map m_pStringMap->SetAt(lpszText, (void*)nString); ASSERT(m_pStringMap->Lookup(lpszText, p)); } // change the toolbar button description TBBUTTON button; GetButton(nIndex, &button); button.iString = nString; SetButton(nIndex, &button); return TRUE; } CString CCJToolBar::GetButtonText(int nIndex) const { CString strResult; GetButtonText(nIndex, strResult); return strResult; } void CCJToolBar::GetButtonText(int nIndex, CString& rString) const { if (m_pStringMap != NULL) { // get button information (need button.iString) TBBUTTON button; GetButton(nIndex, &button); // look in map for matching iString POSITION pos = m_pStringMap->GetStartPosition(); CString str; void* p; while (pos) { m_pStringMap->GetNextAssoc(pos, str, p); if ((int)p == button.iString) { rString = str; return; } } } rString.Empty(); } ///////////////////////////////////////////////////////////////////////////// // CCJToolBar message handlers BEGIN_MESSAGE_MAP(CCJToolBar, CCJToolBarBase) //{{AFX_MSG_MAP(CCJToolBar) ON_WM_NCHITTEST() ON_WM_NCPAINT() ON_WM_PAINT() ON_WM_NCCALCSIZE() ON_WM_WINDOWPOSCHANGING() ON_WM_WINDOWPOSCHANGED() ON_WM_SYSCOLORCHANGE() ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize) ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize) ON_WM_NCCREATE() ON_WM_ERASEBKGND() ON_WM_DESTROY() ON_WM_RBUTTONDOWN() ON_MESSAGE(WM_SETTINGCHANGE, OnPreserveZeroBorderHelper) //}}AFX_MSG_MAP ON_MESSAGE(WM_SETFONT, OnPreserveZeroBorderHelper) //ON_NOTIFY_REFLECT(TBN_DROPDOWN, OnToolBarBtnDropDown) ON_NOTIFY_REFLECT(TBN_BEGINADJUST, OnToolBarBeginAdjust) ON_NOTIFY_REFLECT(TBN_BEGINDRAG, OnToolBarBeginDrag) ON_NOTIFY_REFLECT(TBN_CUSTHELP, OnToolBarCustomHelp) ON_NOTIFY_REFLECT(TBN_ENDADJUST, OnToolBarEndAdjust) ON_NOTIFY_REFLECT(TBN_ENDDRAG, OnToolBarEndDrag) ON_NOTIFY_REFLECT(TBN_GETBUTTONINFO, OnToolBarGetButtonInfo) ON_NOTIFY_REFLECT(TBN_QUERYDELETE, OnToolBarQueryDelete) ON_NOTIFY_REFLECT(TBN_QUERYINSERT, OnToolBarQueryInsert) ON_NOTIFY_REFLECT(TBN_RESET, OnToolBarReset) ON_NOTIFY_REFLECT(TBN_TOOLBARCHANGE, OnToolBarChange) END_MESSAGE_MAP() //////////////// // Override to avoid MFC in case I'm inside a ReBar // BOOL CCJToolBar::OnEraseBkgnd(CDC*) { return (BOOL)Default(); } UINT CCJToolBar::OnNcHitTest(CPoint) { return HTCLIENT; } ////////////////// // Calcluate size of client area. Adds room for grippers // void CCJToolBar::OnNcCalcSize(BOOL /*bCalcValidRects*/, NCCALCSIZE_PARAMS* lpncsp) { // calculate border space (will add to top/bottom, subtract from right/bottom) CRect rect; rect.SetRectEmpty(); BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0; CCJToolBarBase::CalcInsideRect(rect, bHorz); ASSERT(_ComCtlVersion != -1); ASSERT(_ComCtlVersion >= VERSION_IE4 || rect.top >= 2); // adjust non-client area for border space lpncsp->rgrc[0].left += rect.left; lpncsp->rgrc[0].top += rect.top; // previous versions of COMCTL32.DLL had a built-in 2 pixel border if (_ComCtlVersion < VERSION_IE4) lpncsp->rgrc[0].top -= 2; lpncsp->rgrc[0].right += rect.right; lpncsp->rgrc[0].bottom += rect.bottom; } ////////////////// // **PD** // Part 2 of correction for MFC is to recalculate everything when the bar // goes from docked to undocked because the AdjustSize calculation happens // when the bar is in the old state, and thus wrong. After the bar is // docked/undocked, I'll recalculate with the new style and commit the change. // void CCJToolBar::OnBarStyleChange(DWORD dwOldStyle, DWORD dwNewStyle) { // a dynamically resizeable toolbar can not have the CBRS_FLOAT_MULTI ASSERT(!((dwNewStyle & CBRS_SIZE_DYNAMIC) && (m_dwDockStyle & CBRS_FLOAT_MULTI))); // a toolbar can not be both dynamic and fixed in size ASSERT (!((dwNewStyle & CBRS_SIZE_FIXED) && (dwNewStyle & CBRS_SIZE_DYNAMIC))); // CBRS_SIZE_DYNAMIC can not be disabled once it has been enabled ASSERT (((dwOldStyle & CBRS_SIZE_DYNAMIC) == 0) || ((dwNewStyle & CBRS_SIZE_DYNAMIC) != 0)); if (m_hWnd != NULL && ((dwOldStyle & CBRS_BORDER_ANY) != (dwNewStyle & CBRS_BORDER_ANY))) { // recalc non-client area when border styles change SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME); } m_bDelayedButtonLayout = TRUE; if (dwOldStyle != dwNewStyle) { DWORD dwMode = 0; if ((dwNewStyle & CBRS_SIZE_DYNAMIC) && (dwNewStyle & CBRS_FLOATING)) dwMode = LM_HORZ | LM_MRUWIDTH; else if (dwNewStyle & CBRS_ORIENT_HORZ) dwMode = LM_HORZ | LM_HORZDOCK; else dwMode = LM_VERTDOCK; CalcDynamicLayout(-1, dwMode | LM_COMMIT); } } void CCJToolBar::OnNcPaint() { EraseNonClient(); } void CCJToolBar::OnWindowPosChanging(LPWINDOWPOS lpwndpos) { // not necessary to invalidate the borders DWORD dwStyle = m_dwStyle; m_dwStyle &= ~(CBRS_BORDER_ANY); CCJToolBarBase::OnWindowPosChanging(lpwndpos); m_dwStyle = dwStyle; } void CCJToolBar::OnPaint() { if (m_bDelayedButtonLayout) Layout(); Default(); } LRESULT CCJToolBar::OnSetButtonSize(WPARAM, LPARAM lParam) { return OnSetSizeHelper(m_sizeButton, lParam); } LRESULT CCJToolBar::OnSetBitmapSize(WPARAM, LPARAM lParam) { return OnSetSizeHelper(m_sizeImage, lParam); } LRESULT CCJToolBar::OnSetSizeHelper(CSize& size, LPARAM lParam) { //WINBUG: The IE4 version of COMCTL32.DLL supports a zero border, but // only if TBSTYLE_TRANSPARENT is on during the the TB_SETBITMAPSIZE // and/or TB_SETBUTTONSIZE messages. In order to enable this feature // all the time (so we get consistent border behavior, dependent only // on the version of COMCTL32.DLL) we turn on TBSTYLE_TRANSPARENT // whenever these messages go through. It would be nice that in a // future version, the system toolbar would just allow you to set // the top and left borders to anything you please. BOOL bModify = FALSE; ASSERT(_ComCtlVersion != -1); DWORD dwStyle = 0; if (_ComCtlVersion >= VERSION_IE4) { dwStyle = GetStyle(); bModify = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT); } LRESULT lResult = Default(); if (lResult) size = lParam; if (bModify) SetWindowLong(m_hWnd, GWL_STYLE, dwStyle); return lResult; } LRESULT CCJToolBar::OnPreserveZeroBorderHelper(WPARAM, LPARAM lParam) { UNUSED_ALWAYS( lParam ); BOOL bModify = FALSE; ASSERT(_ComCtlVersion != -1); DWORD dwStyle = 0; if (_ComCtlVersion >= VERSION_IE4) { dwStyle = GetStyle(); bModify = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT); } LRESULT lResult = Default(); if (bModify) SetWindowLong(m_hWnd, GWL_STYLE, dwStyle); return lResult; } void CCJToolBar::OnSysColorChange() { // re-color bitmap for toolbar if (m_hInstImageWell != NULL && m_hbmImageWell != NULL) { HBITMAP hbmNew; hbmNew = _LoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell); if (hbmNew != NULL) AddReplaceBitmap(hbmNew); } } ///////////////////////////////////////////////////////////////////////////// // CCJToolBar idle update through CCJToolCmdUI class class CCJToolCmdUI : public CCmdUI // class private to this file ! { public: // re-implementations only virtual void Enable(BOOL bOn); virtual void SetCheck(int nCheck); virtual void SetText(LPCTSTR lpszText); }; void CCJToolCmdUI::Enable(BOOL bOn) { m_bEnableChanged = TRUE; CCJToolBar* pToolBar = (CCJToolBar*)m_pOther; ASSERT(pToolBar != NULL); ASSERT_KINDOF(CCJToolBar, pToolBar); ASSERT(m_nIndex < m_nIndexMax); UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) & ~TBBS_DISABLED; if (!bOn) { nNewStyle |= TBBS_DISABLED; // WINBUG: If a button is currently pressed and then is disabled // COMCTL32.DLL does not unpress the button, even after the mouse // button goes up! We work around this bug by forcing TBBS_PRESSED // off when a button is disabled. nNewStyle &= ~TBBS_PRESSED; } ASSERT(!(nNewStyle & TBBS_SEPARATOR)); pToolBar->SetButtonStyle(m_nIndex, nNewStyle); } void CCJToolCmdUI::SetCheck(int nCheck) { ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate CCJToolBar* pToolBar = (CCJToolBar*)m_pOther; ASSERT(pToolBar != NULL); ASSERT_KINDOF(CCJToolBar, pToolBar); ASSERT(m_nIndex < m_nIndexMax); UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) & ~(TBBS_CHECKED | TBBS_INDETERMINATE); if (nCheck == 1) nNewStyle |= TBBS_CHECKED; else if (nCheck == 2) nNewStyle |= TBBS_INDETERMINATE; ASSERT(!(nNewStyle & TBBS_SEPARATOR)); pToolBar->SetButtonStyle(m_nIndex, nNewStyle | TBBS_CHECKBOX); } void CCJToolCmdUI::SetText(LPCTSTR) { // ignore it } void CCJToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) { CCJToolCmdUI state; state.m_pOther = this; state.m_nIndexMax = (UINT)DefWindowProc(TB_BUTTONCOUNT, 0, 0); for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) { // get buttons state TBBUTTON button; GetButton(state.m_nIndex, &button); state.m_nID = button.idCommand; // ignore separators if (!(button.fsStyle & TBSTYLE_SEP)) { // allow reflections if (CWnd::OnCmdMsg(0, MAKELONG((int)CN_UPDATE_COMMAND_UI, WM_COMMAND+WM_REFLECT_BASE), &state, NULL)) continue; // allow the toolbar itself to have update handlers if (CWnd::OnCmdMsg(state.m_nID, CN_UPDATE_COMMAND_UI, &state, NULL)) continue; // allow the owner to process the update state.DoUpdate(pTarget, bDisableIfNoHndler); } } // update the dialog controls added to the toolbar UpdateDialogControls(pTarget, bDisableIfNoHndler); } ///////////////////////////////////////////////////////////////////////////// // CCJToolBar diagnostics #ifdef _DEBUG void CCJToolBar::AssertValid() const { // Note: CCJToolBarBase::AssertValid is not called because it checks for // m_nCount and m_pData to be in sync, which they are not in CCJToolBar. #ifndef _VC_VERSION_5 ASSERT(m_hbmImageWell == NULL || ( afxData.bWin95 || ::GetObjectType(m_hbmImageWell) == OBJ_BITMAP)); #else ASSERT(m_hbmImageWell == NULL || ( ::GetObjectType(m_hbmImageWell) == OBJ_BITMAP)); #endif if (m_hInstImageWell != NULL && m_hbmImageWell != NULL) ASSERT(m_hRsrcImageWell != NULL); } void CCJToolBar::Dump(CDumpContext& dc) const { CCJToolBarBase::Dump(dc); dc << "m_hbmImageWell = " << (UINT)m_hbmImageWell; dc << "\nm_hInstImageWell = " << (UINT)m_hInstImageWell; dc << "\nm_hRsrcImageWell = " << (UINT)m_hRsrcImageWell; dc << "\nm_sizeButton = " << m_sizeButton; dc << "\nm_sizeImage = " << m_sizeImage; if (dc.GetDepth() > 0) { CCJToolBar* pBar = (CCJToolBar*)this; int nCount = pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0); for (int i = 0; i < nCount; i++) { TBBUTTON button; GetButton(i, &button); dc << "\ntoolbar button[" << i << "] = {"; dc << "\n\tnID = " << button.idCommand; dc << "\n\tnStyle = " << MAKELONG(button.fsStyle, button.fsState); if (button.fsStyle & TBSTYLE_SEP) dc << "\n\tiImage (separator width) = " << button.iBitmap; else dc <<"\n\tiImage (bitmap image index) = " << button.iBitmap; dc << "\n}"; } } dc << "\n"; } #endif #ifdef AFX_INIT_SEG #pragma code_seg(AFX_INIT_SEG) #endif IMPLEMENT_DYNAMIC(CCJToolBar, CCJToolBarBase) /////////////////////////////////////////////////////////////////////////////